package org.dromara.web.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2ServerConfig;
import cn.dev33.satoken.oauth2.consts.GrantType;
import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate;
import cn.dev33.satoken.oauth2.data.model.CodeModel;
import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel;
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel;
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy;
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
import cn.dev33.satoken.oauth2.template.SaOAuth2Util;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.dromara.application.domain.AppUser;
import org.dromara.application.domain.vo.ApplicationVo;
import org.dromara.application.mapper.AppUserMapper;
import org.dromara.application.service.IApplicationService;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.NetUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.service.SysLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static kotlin.reflect.jvm.internal.impl.builtins.StandardNames.FqNames.map;

/**
 * OAuth2认证
 *
 * @author YCJZ
 * @date 2025/09/16
 */
@Slf4j
@RestController
@RequiredArgsConstructor
public class SaOauth2ServerController {

    private final SysLoginService loginService;
    private final SysUserMapper userMapper;
    private final IApplicationService appService;
    private final AppUserMapper appUserMapper;

    // OAuth2-Server 端：处理所有 OAuth2 相关请求
    @SaIgnore
    @RequestMapping("/oauth2/*")
    public Object request() {
        System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
        return SaOAuth2ServerProcessor.instance.dister();
    }

    // Sa-Token OAuth2 定制化配置
    @Autowired
    public void configOAuth2Server(SaOAuth2ServerConfig oauth2Server) {

        // 未登录的视图
        SaOAuth2Strategy.instance.notLoginView = ()-> new ModelAndView("login.html");

        // 登录处理函数
        SaOAuth2Strategy.instance.doLoginHandle = (name, pwd) -> {

            SysUserVo user = loadUserByUsername(name);
            LoginUser loginUser = loginService.buildLoginUser(user);
            SaLoginParameter model = new SaLoginParameter();
            model.setTimeout(3600);
            model.setActiveTimeout(3600);
//            model.setExtra(LoginHelper.CLIENT_KEY, "34ed59f0744b4786bc7fd5ec44bc8e11");
            LoginHelper.login(loginUser, model);
            StpUtil.login(user.getUserId());
            return SaResult.ok().set("satoken", StpUtil.getTokenValue());
        };

        // 授权确认视图
        SaOAuth2Strategy.instance.confirmView = (clientId, scopes)->{
            Map<String, Object> map = new HashMap<>();
            map.put("clientId", clientId);
            map.put("scope", scopes);
            map.put("satoken", StpUtil.getTokenValue());
            return new ModelAndView("confirm.html", map);
        };

    }

    private SysUserVo loadUserByUsername(String username) {
        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
        if (ObjectUtil.isNull(user)) {
            log.info("登录用户：{} 不存在.", username);
            throw new UserException("user.not.exists", username);
        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
            log.info("登录用户：{} 已被停用.", username);
            throw new UserException("user.blocked", username);
        }
        return user;
    }

    @GetMapping("/oauth2/getRedirectUri")
    public R<String> getRedirectUri(Long appId){
        ApplicationVo applicationVo = appService.queryById(appId);
        if (ObjectUtil.isNull(applicationVo)) {
            return R.fail("未查询到该应用！");
        }
//        LoginUser loginUser = LoginHelper.getLoginUser();
//
//        Map<String, Object> map = new HashMap<>();
//        map.put("preferred_username", loginUser.getUsername());
//        map.put("nickname", loginUser.getNickname());
//        map.put("avatar", "");
//        map.put("sex", "");
//        AppUser appUser = appUserMapper.selectOne(new LambdaQueryWrapper<AppUser>()
//            .eq(AppUser::getUserId, loginUser.getUserId())
//            .eq(AppUser::getClientId, applicationVo.getClientId()));
//        map.put("openId", appUser.getOpenId());
//        SaResult saResult = SaResult.ok().setMap(map);

        List<String> scopes = Arrays.asList("openid", "unionid", "userid", "userinfo", "oidc");

        String code = SaOAuth2Strategy.instance.createCodeValue.execute(applicationVo.getClientId(), LoginHelper.getUserId(), scopes);
        CodeModel codeModel = new CodeModel(code, applicationVo.getClientId(), scopes, LoginHelper.getUserId(), applicationVo.getCallbackUrl(), null);
        SaOAuth2Manager.getDao().saveCode(codeModel);
        SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();

        URL url = URLUtil.url(applicationVo.getHomePage());
        String host = url.getHost();
        int port = url.getPort();
        Map<String, String> map1 = new HashMap<>();
        map1.put("tenantId", "000000");
        map1.put("domain", host + ":" + port);
        map1.put("state", AuthStateUtils.createState());
        map1.put("authType", "login");

        String s = dataGenerate.buildRedirectUri(applicationVo.getCallbackUrl(), code, Base64.encode(JsonUtils.toJsonString(map1), StandardCharsets.UTF_8));

        return R.ok("", s);

    }

}
